﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Configuration;
using GalaSoft.MvvmLight;
using System.Collections.ObjectModel;
using System.Windows.Controls;
using System.Runtime.Serialization;
using System.Xml.Serialization;

namespace AutoCorrelatorGUI.Model
{
   [SettingsSerializeAs(SettingsSerializeAs.Xml)]
   public class StatsColumnManager : ObservableObject
   {
      public static BindingList<StatsColumnItem> Generate()
      {
         var cols = new BindingList<StatsColumnItem>();

         cols.Add(new StatsColumnItem("All", "", "")
         {
            Expanded = true,
            Children =
            {
               new StatsColumnItem("Input Data", "", "")
               {
               },

               new StatsColumnItem("Output Data", "", "")
               {
               },

               new StatsColumnItem("Peak/Trough Data", "", "")
               {
                  Children =
                  {
                     new StatsColumnItem("Zero Xs"     , "Analysis.ZeroCrossings", "G5", Properties.Resources.TT_Analysis_ZeroCrossings),
                     new StatsColumnItem("Max AMP"     , "Analysis.Peak0Val     ", "G5"),
                     new StatsColumnItem("Trough 1 Val", "Analysis.Trough1Val   ", "G5", Properties.Resources.TT_Analysis_TroughVal),
                     new StatsColumnItem("Trough 1 Hz" , "Analysis.Trough1Hz    ", "G5", Properties.Resources.TT_Analysis_TroughHz),
                     new StatsColumnItem("Trough 2 Val", "Analysis.Trough2Val   ", "G5", Properties.Resources.TT_Analysis_TroughVal),
                     new StatsColumnItem("Trough 2 Hz" , "Analysis.Trough2Hz    ", "G5", Properties.Resources.TT_Analysis_TroughHz),
                     new StatsColumnItem("Trough 3 Val", "Analysis.Trough3Val   ", "G5", Properties.Resources.TT_Analysis_TroughVal),
                     new StatsColumnItem("Trough 3 Hz" , "Analysis.Trough3Hz    ", "G5", Properties.Resources.TT_Analysis_TroughHz),
                     new StatsColumnItem("Trough 4 Val", "Analysis.Trough4Val   ", "G5", Properties.Resources.TT_Analysis_TroughVal),
                     new StatsColumnItem("Trough 4 Hz" , "Analysis.Trough4Hz    ", "G5", Properties.Resources.TT_Analysis_TroughHz),
                     new StatsColumnItem("Peak 1 Val"  , "Analysis.Peak1Val     ", "G5", Properties.Resources.TT_Analysis_PeakVal),
                     new StatsColumnItem("Peak 1 Hz"   , "Analysis.Peak1Hz      ", "G5", Properties.Resources.TT_Analysis_PeakHz),
                     new StatsColumnItem("Peak 2 Val"  , "Analysis.Peak2Val     ", "G5", Properties.Resources.TT_Analysis_PeakVal),
                     new StatsColumnItem("Peak 2 Hz"   , "Analysis.Peak2Hz      ", "G5", Properties.Resources.TT_Analysis_PeakHz),
                     new StatsColumnItem("Peak 3 Val"  , "Analysis.Peak3Val     ", "G5", Properties.Resources.TT_Analysis_PeakVal),
                     new StatsColumnItem("Peak 3 Hz"   , "Analysis.Peak3Hz      ", "G5", Properties.Resources.TT_Analysis_PeakHz),
                     new StatsColumnItem("Peak 4 Val"  , "Analysis.Peak4Val     ", "G5", Properties.Resources.TT_Analysis_PeakVal),
                     new StatsColumnItem("Peak 4 Hz"   , "Analysis.Peak4Hz      ", "G5", Properties.Resources.TT_Analysis_PeakHz)
                  }
               },

               new StatsColumnItem("Comparison Data", "", "")
               {
                  Children =
                  {
                     new StatsColumnItem("Output Correlation"                  , "Comparison.OutputCorrelation     ", "G5", Properties.Resources.TT_Diff_Correlation),
                     new StatsColumnItem("Output RMS Total"                    , "Comparison.OutputRMSTotal        ", "G5", Properties.Resources.TT_Diff_RMS),
                     new StatsColumnItem("Output Area"                         , "Comparison.OutputArea            ", "G5", Properties.Resources.TT_Diff_Area),
                     new StatsColumnItem("Output Max Diff"                     , "Comparison.OutputMaxDiff         ", "G5", Properties.Resources.TT_Diff_Max),
                     new StatsColumnItem("FFT Correlation"                     , "Comparison.FFTCorrelation        ", "G5", Properties.Resources.TT_Diff_Correlation),
                     new StatsColumnItem("FFT RMS Total"                       , "Comparison.FFTRMSTotal           ", "G5", Properties.Resources.TT_Diff_RMS),
                     new StatsColumnItem("FFT Area"                            , "Comparison.FFTArea               ", "G5", Properties.Resources.TT_Diff_Area),
                     new StatsColumnItem("FFT Max Diff"                        , "Comparison.FFTMaxDiff            ", "G5", Properties.Resources.TT_Diff_Max),
                     new StatsColumnItem("Wavelet Correlation"                 , "Comparison.WaveletCorrelation    ", "G5", Properties.Resources.TT_Diff_Correlation),
                     new StatsColumnItem("Wavelet RMS Total"                   , "Comparison.WaveletRMSTotal       ", "G5", Properties.Resources.TT_Diff_RMS),
                     new StatsColumnItem("Wavelet Area"                        , "Comparison.WaveletArea           ", "G5", Properties.Resources.TT_Diff_Area),
                     new StatsColumnItem("Wavelet Max Diff"                    , "Comparison.WaveletMaxDiff        ", "G5", Properties.Resources.TT_Diff_Max),
                  }
               },

               new StatsColumnItem("Input Data Stats", "", "")
               {
                  Children =
                  {
                     new StatsColumnItem("Input Mean"              , "Input.Stats.Mean",     "G5"),
                     new StatsColumnItem("Input Standard Error"    , "Input.Stats.StdError", "G5"),
                     new StatsColumnItem("Input Median"            , "Input.Stats.Median",   "G5"),
                     new StatsColumnItem("Input Mode"              , "Input.Stats.Mode",     "G5"),
                     new StatsColumnItem("Input Standard Deviation", "Input.Stats.StdDev",   "G5"),
                     new StatsColumnItem("Input Variance"          , "Input.Stats.Variance", "G5"),
                     new StatsColumnItem("Input Kurtosis"          , "Input.Stats.Kurtosis", "G5"),
                     new StatsColumnItem("Input Skewness"          , "Input.Stats.Skewness", "G5"),
                     new StatsColumnItem("Input Range"             , "Input.Stats.Range",    "G5"),
                     new StatsColumnItem("Input Minimum "          , "Input.Stats.Minimum",  "G5"),
                     new StatsColumnItem("Input Maximum "          , "Input.Stats.Maximum",  "G5"),
                     new StatsColumnItem("Input Sum"               , "Input.Stats.Sum",      "G5"),
                     new StatsColumnItem("Input Count"             , "Input.Stats.Count",    "G5"),
                     new StatsColumnItem("Input Norm"              , "Input.Stats.Norm",     "G5")
                  }
               },

               new StatsColumnItem("Output Data Stats", "", "")
               {
                  Children =
                  {
                     new StatsColumnItem("Output Mean"              , "Output.Stats.Mean",     "G5"),
                     new StatsColumnItem("Output Standard Error"    , "Output.Stats.StdError", "G5"),
                     new StatsColumnItem("Output Median"            , "Output.Stats.Median",   "G5"),
                     new StatsColumnItem("Output Mode"              , "Output.Stats.Mode",     "G5"),
                     new StatsColumnItem("Output Standard Deviation", "Output.Stats.StdDev",   "G5"),
                     new StatsColumnItem("Output Variance"          , "Output.Stats.Variance", "G5"),
                     new StatsColumnItem("Output Kurtosis"          , "Output.Stats.Kurtosis", "G5"),
                     new StatsColumnItem("Output Skewness"          , "Output.Stats.Skewness", "G5"),
                     new StatsColumnItem("Output Range"             , "Output.Stats.Range",    "G5"),
                     new StatsColumnItem("Output Minimum "          , "Output.Stats.Minimum",  "G5"),
                     new StatsColumnItem("Output Maximum "          , "Output.Stats.Maximum",  "G5"),
                     new StatsColumnItem("Output Sum"               , "Output.Stats.Sum",      "G5"),
                     new StatsColumnItem("Output Count"             , "Output.Stats.Count",    "G5"),
                     new StatsColumnItem("Output Norm"              , "Output.Stats.Norm",     "G5")
                  }
               }
            }
         });

         return cols;
      }

      public StatsColumnManager()
      {
      }

      private BindingList<StatsColumnItem> _Items = null;

      public BindingList<StatsColumnItem> Items
      {
         get
         {
            return _Items;
         }
         set
         {
            //Need to move the event handlers
            if (_Items != null)
            {
               _Items.ListChanged -= Items_ListChanged;

               foreach (var item in value)
               {
                  item.CheckedChanged -= Items_CheckedChanged;
               }
            }

            _Items = value;

            if (_Items != null)
            {
               foreach (var item in value)
               {
                  item.CheckedChanged += Items_CheckedChanged;
               }

               _Items.ListChanged += Items_ListChanged;
            }
         }
      }

      void Items_ListChanged(object sender, ListChangedEventArgs e)
      {
         if (e.ListChangedType == ListChangedType.ItemAdded)
            ((sender as BindingList<StatsColumnItem>)[e.NewIndex] as StatsColumnItem).CheckedChanged += Items_CheckedChanged;
         else if (e.ListChangedType == ListChangedType.ItemDeleted)
            ((sender as BindingList<StatsColumnItem>)[e.NewIndex] as StatsColumnItem).CheckedChanged -= Items_CheckedChanged;
      }

      void Items_CheckedChanged(object sender, EventArgs e)
      {
         StatsColumnItem item = sender as StatsColumnItem;

         if (item == null)
            return;

         if (item.IsChecked.HasValue && item.GetColumn() != null)
         {
            if (item.IsChecked.Value)
            {
               if (!Columns.Contains(item.GetColumn()))
               {
                  Columns.Add(item.GetColumn());
               }
            }
            else
            {
               Columns.Remove(item.GetColumn());
            }
         }
      }

      /// <summary>
      /// The <see cref="Columns" /> property's name.
      /// </summary>
      public const string ColumnsPropertyName = "Columns";

      private ObservableCollection<DataGridColumn> _Columns = null;

      /// <summary>
      /// Sets and gets the Columns property.
      /// Changes to that property's value raise the PropertyChanged event. 
      /// </summary>
      [XmlIgnore()]
      public ObservableCollection<DataGridColumn> Columns
      {
         get
         {
            return _Columns;
         }

         set
         {
            if (_Columns == value)
            {
               return;
            }

            _Columns = value;
            RaisePropertyChanged(ColumnsPropertyName);

            if (_Items == null)
               Items = Generate();

            SyncColumns();
         }
      }

      StatsColumnItem FindItem(string name, BindingList<StatsColumnItem> list)
      {
         foreach (var item in list)
         {
            if (item.Header == name)
               return item;

            var foundItem = FindItem(name, item.Children);

            if (foundItem != null)
               return foundItem;
         }

         return null;
      }

      public void SetIOColumnCount(int inCount, int outCountLow, int outCountHigh)
      {
         var inCols = FindItem("Input Data", Items).Children;

         while (inCols.Count > inCount + 1)
         {
            inCols.RemoveAt(inCols.Count - 1);
         }

         for (int i = inCols.Count; i < inCount; i++)
         {
            inCols.Add(new StatsColumnItem("Input[" + i.ToString() + "]", "InputData[" + i.ToString() + "].Y", "G8"));
         }

         var outCols = FindItem("Output Data", Items).Children;

         outCols.Clear();

         for (int i = outCountLow; i <= outCountHigh; i++)
         {
            outCols.Add(new StatsColumnItem("Output[" + i.ToString() + "]", "OutputData[" + i.ToString() + "]", "G8"));
         }
      }

      public void ClearChecks()
      {
         foreach (var item in Items)
         {
            item.IsChecked = false; //Should clear all children as well
         }
      }

      private void SyncColumns()
      {
         var selectedCols = new List<DataGridColumn>();

         foreach (var item in _Items)
         {
            selectedCols.AddRange(item.GetAllChildColumns());
         }

         selectedCols.Add(Columns[0]); //Always add in the index column

         List<DataGridColumn> toRemove = new List<DataGridColumn>();
         List<DataGridColumn> toAdd = new List<DataGridColumn>();

         foreach (var col in Columns)
         {
            if (!selectedCols.Contains(col))
            {
               toRemove.Add(col);
            }
         }

         foreach (var col in selectedCols)
         {
            if (!Columns.Contains(col))
            {
               toAdd.Add(col);
            }
         }

         foreach (var col in toRemove)
         {
            Columns.Remove(col);
         }

         foreach (var col in toAdd)
         {
            Columns.Add(col);
         }
      }
   }
}
